#ifndef NE_STL_INCLUDENE_STL_SPINLOCK_H
#define NE_STL_INCLUDENE_STL_SPINLOCK_H

#include "ne_stl_atomic.h"
#include "ne_stl_build.h"
#include "ne_stl_value_def.h"

#define NE_STL_SPINLOCK_USE_THRD_YIELD 1
#if (NE_STL_SPINLOCK_USE_THRD_YIELD)
#ifdef NE_STL_OS_WIN
#if defined(NE_STL_USE_MSVC)
#if (_MSC_VER < 1920)
#include <thr/xthreads.h>
#else
#include <xthreads.h>
#endif
#else
//#include <xthreads.h>
#endif
#define THRD_YIELD() _Thrd_yield()
#else
#if defined(NE_STL_OS_ANDROID) || defined(NE_STL_OS_LINUX)
#include <sched.h>
#define THRD_YIELD() sched_yield()
#else
#include <pthread.h>
// #define THRD_YIELD() pthread_yield()
#define THRD_YIELD() pthread_yield_np()
#endif
#endif
#else
#define THRD_YIELD()
#endif
namespace nstd {

namespace {

enum class lock_state : int32_t { lock_state_begin, lock_state_unlocked, lock_state_locked, lock_state_end };

struct lock_locked : integral_constant<int32_t, static_cast<int32_t>(lock_state::lock_state_locked)> {};
struct lock_unlocked : integral_constant<int32_t, static_cast<int32_t>(lock_state::lock_state_unlocked)> {};

}  // namespace
class _spinlock {
public:
    _spinlock()
        : value_{lock_unlocked::value} {}
    inline void lock() {
        while (!value_.compare_exchange(lock_unlocked::value, lock_locked::value)) {
            THRD_YIELD();
        }
    }
    inline void unlock() { value_.store(lock_unlocked::value); }
    _spinlock& operator=(const _spinlock& other) = delete;

private:
    nstd::atomic_int value_;
};
using spinlock = _spinlock;

class _lock_guard {
public:
    explicit _lock_guard(spinlock& lock)
        : lock_(lock) {
        lock_.lock();
    }
    ~_lock_guard() { lock_.unlock(); }
    _lock_guard& operator=(const _lock_guard& other) = delete;

private:
    nstd::spinlock& lock_;
};
using lock_guard = _lock_guard;

}  // namespace nstd

#endif  // NE_STL_INCLUDENE_STL_SPINLOCK_H
